﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO.Ports;
using System.Threading;
using System.Windows.Threading;

namespace CommanderUI
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>
        /// Порт.
        /// </summary>
        SerialPort _port = null;

        readonly DispatcherTimer _timer;

        /// <summary>
        /// 
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
            _timer = new DispatcherTimer(DispatcherPriority.Normal, Dispatcher);
            _timer.Interval = new TimeSpan(0, 0, 0, 0, 100); // Send commands with 100ms interval.
            _timer.Tick += _timer_Tick;
            _timer.Start();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_ConnectComm_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (_port != null)
                {
                    _label_CommStatus.Content = "Offline";
                    _label_CommStatus.Background = Brushes.LightPink;
                    _port.DataReceived -= onPortDataReceived;
                    _port.Close();
                    _port.Dispose();
                    _port = null;
                    return;
                }
                _port = new SerialPort(_textBox_CommPort.Text, Int32.Parse(_textBox_CommPortSpeed.Text));
                _port.Parity = Parity.None;
                _port.StopBits = StopBits.One;
                _port.DataBits = 8;
                _port.Handshake = Handshake.None;
                _port.DataReceived += onPortDataReceived;
                _port.Open();
                _label_CommStatus.Content = "Online";
                _label_CommStatus.Background = Brushes.LightGreen;
            }
            catch
            {
                _label_CommStatus.Content = "Error";
                ConsoleWriteString("Error");
                _label_CommStatus.Background = Brushes.Red;
                _port = null;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="s"></param>
        void ConsoleWriteString(string s, params object[] o)
        {
            _console.Text += String.Format(s, o); ;
            _console.Text += Environment.NewLine;
            _console.ScrollToEnd();
        }

        /// <summary>
        /// Отправка комманды на устройство.
        /// </summary>
        /// <param name="s"></param>
        void CommSendCommand(string s, params object[] o)
        {
            string str = String.Format(s, o);
            if (_port != null)
            {
                _port.Write(str);
            }
            ConsoleWriteString("Sending: {0}", str);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void onPortDataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            if (e.EventType == System.IO.Ports.SerialData.Chars)
            {
                Console.Write(_port.ReadExisting());
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SendButton_Click(object sender, RoutedEventArgs e)
        {
            ConsoleWriteString(_input.Text);
            CommSendCommand(_input.Text);
            _input.Text = "";
        }

        #region Engine Controlling

        /// <summary>
        /// Определяет ведеться ли захват мыши.
        /// </summary>
        bool _capturing = false;

        private void UserControl_MouseDown(object sender, MouseButtonEventArgs e)
        {
            _capturing = true;
            UserControl_MouseMove(sender, e);
            _moutionCanvas.CaptureMouse();
            _moutionCanvas.Cursor = Cursors.SizeAll;
            
        }

        private void UserControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (_capturing)
            {
                const double R = 128;
                var p = e.GetPosition(_moutionCanvas);
                double x = p.X - R;
                double y = R - p.Y;
                double norm = Math.Sqrt(x * x + y * y);
                if (norm > R)
                {
                    double k = R / norm;
                    x *= k;
                    y *= k;
                }
                double mX = x + R;
                double mY = R - y;
                //updateEngineState((int)x, (int)y);
                _engineX = (int)x;
                _engineY = (int)y;
                _engineUpdated = true;
                Canvas.SetTop(_centerRect, mY-4);
                Canvas.SetLeft(_centerRect, mX-4);
            }
        }

        int _engineX;
        int _engineY;
        bool _engineUpdated = false;

        void _timer_Tick(object sender, EventArgs e)
        {
            if (_engineUpdated)
            {
                _engineUpdated = false;
                updateEngineState(_engineX, _engineY);
            }
        }

        private void UserControl_MouseUp(object sender, MouseButtonEventArgs e)
        {
            _moutionCanvas.Cursor = Cursors.Arrow;
            _moutionCanvas.ReleaseMouseCapture();
            Canvas.SetTop(_centerRect, 128-4);
            Canvas.SetLeft(_centerRect, 128-4);
            updateEngineState(0, 0);
            _capturing = false;
        }

        /// <summary>
        /// Перевод положения точки на экране в состояние двигателей.
        /// </summary>
        /// <param name="x">Координата X</param>
        /// <param name="y">Координата Y</param>
        void updateEngineState(int x, int y)
        {
            int L = 0;
            int R = 0;
            L = y + x;
            R = y - x;
            // BUG: can be more than 128 here
            // Ned to be fixed.
            char ls = L > 0 ? '+' : '-';
            char rs = R > 0 ? '+' : '-';

            CommSendCommand("ML{0}{1:X2}", ls, (byte)Math.Abs(L*1.9));
            CommSendCommand("MR{0}{1:X2}", rs, (byte)Math.Abs(R*1.9));
            _leftEngineState.Content = L;
            _righrEngineState.Content = R;
        }

        #endregion
    }
}
